﻿
#define DebugParallel_Bug_LockConvoy

namespace TestForDebug
{
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using SDFL.Utilities;

    class Program
    {
        static void Main(string[] args)
        {

#if BasicDebug
            // Breakpoint Condition: name.Equals("Name3")
            // Data Tip - Pin / Drag-Drop / Last Session Debugging value / Change Value
           
            string[] strNames = { "Name1", "Name2", "Name3", "Name4", "Name5" };

            foreach (string name in strNames)
            {
                var temp = Foo1(name);
                Console.WriteLine(temp);
            }

            int tempInt = 5;
            for (int i = 0; i < 10; i++)
            {
                if (i > 6)
                {
                    tempInt = 5;
                }
            }

#elif DebugThread
            // Ctrl + D, T
            // Flag Just My Code
            // Breakpoint filter - ThreadName = "Thread 2"

            Thread t = new Thread(new ThreadStart(Go));
            t.Name = "Thread 1";

            Thread t2 = new Thread(new ThreadStart(Go));
            t2.Name = "Thread 2";

            t.Start();
            t2.Start();
            Go();
 
         
#elif DebugIntelliTrace
            // set it first
            Console.WriteLine("IntelliTrae Test");
            CallTestMethod(5);


#elif DebugParallel
            // About Parallel programing http://msdn.microsoft.com/en-us/library/dd460693.aspx
            /* Many personal computers and workstations have two or four cores (that is, CPUs) that enable multiple threads to be executed simultaneously. 
             * Computers in the near future are expected to have significantly more cores. 
             * To take advantage of the hardware of today and tomorrow, you can parallelize your code to distribute work across multiple processors. 
             * In the past, parallelization required low-level manipulation of threads and locks. 
             * Visual Studio 2010 and the .NET Framework 4 enhance support for parallel programming by providing a new runtime, new class library types, and new diagnostic tools.
             * These features simplify parallel development so that you can write efficient, fine-grained, and scalable parallel code in a natural idiom without having to work directly with threads or the thread pool. 
             * The following illustration provides a high-level overview of the parallel programming architecture in the .NET Framework 4.*/

            // Debug - "Parallel Tasks" and "Parallel Stack"
            var task_a = Task.Factory.StartNew(() => DoSomeWork(10000));
            var task_b = Task.Factory.StartNew(() => DoSomeWork(5000));
            var task_c = Task.Factory.StartNew(() => DoSomeWork(1000));

            Task.WaitAll(task_a, task_b, task_c);


#elif DebugParallel_Bug_Prime 

            // try to find the bug by using parallel debug feature 
            // 1. Conditions [ numberToTest.Equals(9) || numberToTest.Equals(15)  ]
            // 2. Free/thaw

            // PLINQ
            var primes =
                    from n in Enumerable.Range(1,100)
                    .AsParallel()
                    .AsOrdered()
                    .WithMergeOptions(ParallelMergeOptions.NotBuffered)
                    where IsPrime(n)
                    select n;
            
            foreach (var prime in primes) 
                Console.Write(prime + ", ");

            WatchDog.Break();
#elif DebugParallel_Bug_Dependency

            // Find the bug.  Some work is getting run before its dependencies are complete. Why?
            // 
            // Steps:
            // 1. Run and wait for the debugger to break in.
            // 2. View the Parallel Tasks window.  Note which tasks are scheduled.
            
            var step1a = Task.Factory.StartNew(Step1a);
            var step1b = Task.Factory.StartNew(Step1b);
            var step1c = Task.Factory.StartNew(Step1c);

            Task.WaitAll(step1a, step1b, step1c);

            var step2a = Task.Factory.StartNew(Step2a);
            var step2b = Task.Factory.StartNew(Step2b);
            var step2c = Task.Factory.StartNew(Step2c);

            Task.WaitAll(step2a, step1b, step1c);

            if (finishedCount == 6)
            {
                Console.WriteLine("Finished!");
            }
            else
            {
                Console.WriteLine("Not Finished!");
            }

            var step3a = Task.Factory.StartNew(Step3a);
            var step3b = Task.Factory.StartNew(Step3b);
            var step3c = Task.Factory.StartNew(Step3c);

            WatchDog.Break();
            Task.WaitAll(step3a, step3b, step3c);



#elif Debug_Lock
                
            // about lock, don't lock(this), lock("string")
            lock ("my lock")
            {
                ManualResetEvent evt = new ManualResetEvent(false);
                WorkerObject worker = new WorkerObject(evt);
                Thread t = new Thread(new ThreadStart(worker.Work));
                t.Start();
                evt.WaitOne();
            }

#elif DebugParallel_Bug_Deadlock           

            // 1. Run and wait for the debugger to break in.
            // 2. Observe the Parallel Tasks window and what each Task is blocked on.
            int transfersCompleted = 0;
            WatchDog.BreakIfRepeats(() => transfersCompleted, 500);

            BankAccount a = new BankAccount { Balance = 1000 };
            BankAccount b = new BankAccount { Balance = 1000 };

            while (true)
            {
                Parallel.Invoke(
                    () => Transfer(a, b, 100),
                    () => Transfer(b, a, 100));

                transfersCompleted += 2;
            }

#elif DebugParallel_Bug_LockConvoy
            // Find the performance bottleneck.  We're going on a convoy!
            // Tool concurrency profiler can find the issue, also Parallel Stacks can find it as well
            // Steps:
            // 1. Run, and wait for the debugger to break in.
            // 2. Observe in the Parallel Stacks window where all of the "Demo" threads are blocked

            WatchDog.BreakIn(2000);

            object obj = new object();

            Enumerable.Range(1, 10).Select(i =>
            {
                var t = new Thread(() =>
                {
                    while (true)
                    {
                        DoWork();

                        lock (obj)
                            DoProtectedWork();
                    }
                })
                {
                    Name = "Demo" + i
                };

                t.Start();
                return t;
            }).ToList().ForEach(t => t.Join());


#else

#endif

        }

        class WorkerObject
        {
            private ManualResetEvent _evt;
            public WorkerObject(ManualResetEvent evt)
            {
                _evt = evt;
            }
            public void Work()
            {
                lock ("my lock")
                {
                    Console.WriteLine("worked.");
                    _evt.Set();
                }
            }
        }


        private static void DoWork() { Thread.Sleep(100); }
        private static void DoProtectedWork() { Thread.Sleep(200); }

        class BankAccount
        {
            public int Balance;
        }

        static void Transfer(BankAccount one, BankAccount two, int amount)
        {
            lock (one) // WARNING: Buggy!
            {
                lock (two)
                {
                    one.Balance -= amount;
                    two.Balance += amount;
                }
            }
        }



        static void Step1a() { Thread.SpinWait(WORK); finishedCount++; }
        static void Step1b() { Thread.SpinWait(WORK); finishedCount++; }
        static void Step1c() { Thread.SpinWait(WORK); finishedCount++; }

        static void Step2a() { Thread.SpinWait(WORK); finishedCount++; }
        static void Step2b() { Thread.SpinWait(WORK); finishedCount++; }
        static void Step2c() { Thread.SpinWait(WORK); finishedCount++; }

        static void Step3a() { Thread.SpinWait(WORK); finishedCount++; }
        static void Step3b() { Thread.SpinWait(WORK); finishedCount++; }
        static void Step3c() { Thread.SpinWait(WORK); finishedCount++; }

        static int finishedCount = 0;
        const int WORK = int.MaxValue;

        public static bool IsPrime(int numberToTest) // WARNING: Buggy!
        {
            // 2 is a weird prime: it’s even. Test for it explicitly.
            if (numberToTest == 2)
                return true;

            // Anything that’s less than 2 or that’s even is not prime
            if (numberToTest < 2 || (numberToTest & 1) == 0)
                return false;

            // Test all odd numbers less than the sqrt of the target number.
            // If the target is divisible by any of them, it’s not prime.
            // We don’t test evens, because if the target is divisible
            // by an even, the target is also even, which we already checked for.
            int upperBound = (int)Math.Sqrt(numberToTest);

            for (int i = 3; i < upperBound; i += 2)
            {
                if ((numberToTest % i) == 0)
                    return false;
            }

            // It’s prime!
            return true;
        }

        private static void CallTestMethod(int testValue)
        {
            Console.WriteLine("In CallTestMethod : " + testValue);
            Console.WriteLine("Last statement...");

        }

        private static void DoSomeWork(int time)
        {
            Thread.Sleep(time);
        }

        private static void Go()
        {
            Console.WriteLine("Hello!");
        }


        internal static string Foo1(string str)
        {
            return str + 1;
        }



        internal static string Foo2(string str)
        {
            return str + 2;
        }
    }
}
